﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Big2.CardsLib;
using Big2.Big2Game;
using System.ServiceModel;
using System.Threading;



namespace Big2.Big2WcfService
{
    [Serializable]
    public class Game
    {
        #region variables
        //public List<Player> Players { get; set; }
        public List<Player> PlayersAndChatters{get;set;}
        public Deck Deck {get; set;}
        public Table Table { get; set; }
        //public ServiceHost Server {get;set; }
        public Big2ServerService Server;
        public Rules GameRules { get; set;}
        Thread thread;
        private int numPlayerReady;
        #endregion

        #region constructors
        public Game()
        {
            //Players = new List<Player>();
            PlayersAndChatters = new List<Player>();
            Deck = new Deck();
            Table = new Table();
            numPlayerReady = 0;
            
        }

        

        
#endregion


        public void StartGame()
        {
            if (Table.Players.Count < 2)
                throw new Exception("Not enough players to start game.");

            //TODO:
            InitGame();
            DealCardsToPlayers();
            StartTurns();
        }        
        private void InitGame()
        {
            //TODO: clear out player's hand, selected cards etc
            //for (int i = 0; i < Table.Players.Count; i++)
            //{
            //    Table.Players.ElementAt(i).Hand.CardsList.Clear();
            //    Table.Players.ElementAt(i).Hand.SelectedCards.Clear();
            //}
            
            Deck.shuffleDeck();
            Table.resetTable();
        }
        public void InitGameServer()
        {
            Server = new Big2ServerService();
           
            Big2ServerService.LoginCalled+=new Big2ServerService.LoginEventHandler(Big2Server_LoginCalled);
            Big2ServerService.LogoutCalled += new Big2ServerService.LogoutEventHandler(Big2Server_LogoutCalled);
            Big2ServerService.PassCalled += new Big2ServerService.PassEventHandler(Big2Server_PassCalled);
            Big2ServerService.PlayCardCalled += new Big2ServerService.PlayCardEventHandler(Big2Server_PlayCardCalled);
           
            Big2ServerService.JoinGameCalled+=new Big2ServerService.JoinGameEventHandler(Big2Server_JoinGameCalled);
            Big2ServerService.TalkCalled += new Big2ServerService.TalkEventHandler(Big2Server_TalkCalled);
            Big2ServerService.StartGameCalled += new Big2ServerService.StartGameEventHandler(Big2ServerService_StartGameCalled);
            
        }

        
       
        //add a player to the current game
        public void AddPlayerToRoom(string name)
        {
            if (PlayersAndChatters.Count > 10)
                throw new Exception("Can not add player to room - room is full");
            Player p = new Player(name);
            AddPlayerToRoom(p);
        }
        public void AddPlayerToRoom(Player p)
        {
            if (PlayersAndChatters.Count > 10)
                throw new Exception("Can not add player to room - room is full.");

            PlayersAndChatters.Add(p);
        }

        //add a player to the current game
        public void AddPlayerToGame(string name)
        {
            if (Table.Players.Count > 3)
                throw new Exception("Can not add player to add - game has enough players.");
            Player p = new Player(name);
            AddPlayerToGame(p);
        }
        public void AddPlayerToGame(Player p)
        {
            if (Table.Players.Count > 3)
                throw new Exception("Can not add player to add - game has enough players.");
            Table.Players.Add(p);
        }

        #region game play methods
        
        private void StartTurns()
        {
            Table.CurrentTurnPlayerName = FindFirstPlayer(1).Name;
            Server.PlayerTurn(Table.CurrentTurnPlayerName, null);
        }
        private void NextPlayerTurn(Player player,Play play)
        {
            Table.CurrentTurnPlayerName = player.Name;
            Table.CurrentPlay = play;
            Server.PlayerTurn(player.Name, play);
        }

        private void PlayerWins(Player winner)
        {
            CalculateScores(winner);
            
            //notify players that someone has won
            Server.GameEnd(winner.Name, Table.Players);

            //start game again
            //StartGame();
        }
        private void CalculateScores(Player winner)
        {
            ///return;

            int chipsLost = 0;
            int chipsForWinner = 0;

            //multiply the betAmt by the number of cards for each player
            for (int i = 0; i < Table.Players.Count; i++)
            {
                if (!Table.Players.ElementAt(i).Name.Equals(winner.Name))
                {
                    chipsLost = GameRules.GetAmount * Table.Players.ElementAt(i).Hand.CardsLeft();
                    Table.Players.ElementAt(i).PreviousScore = Table.Players.ElementAt(i).Score;
                    Table.Players.ElementAt(i).Score = Table.Players.ElementAt(i).PreviousScore - chipsLost;

                    chipsForWinner += chipsLost;
                }//if
            }//for

            winner.PreviousScore = winner.Score;
            winner.Score += chipsForWinner;
            
            //maybe popup this in a window or something
        }

        //return position of the player in the list
        private Player FindFirstPlayer(int type)
        {
            //find first player to go first
            //if type == 1 winner from previous game 
            //if type == 2 smallest card goes first

            if (type == 1)
            {
                for(int i = 0; i < this.Table.Players.Count; i++)
                {
                    if (this.Table.Players.ElementAt(i).WonLast)
                        return this.Table.Players.ElementAt(i);
                        //return i;
                }//for
            }//if
            else
            {
                //define smallest card
                Card smallest = new Card(CardRank.Three, CardSuit.Diamonds);

                for (int i = 0; i < this.Table.Players.Count; i++)
                {
                    for (int j = 0; j < this.Table.Players.ElementAt(i).Hand.CardsList.Count; j++)
                    {
                        if (this.Table.Players.ElementAt(i).Hand.CardsList.ElementAt(j).isSameCard(smallest))
                            return this.Table.Players.ElementAt(i);
                            //return i;
                    }//inner for
                }//outer for
            }//else
            return Table.Players[0];
        }//Player FindFirstPlayer(int type)

        //return the next player
        private Player FindNextPlayer(string PrevPlayerName)
        {
            int i = 0;

            for (i = 0; i < this.Table.Players.Count; i++)
            {
                if (this.Table.Players.ElementAt(i).Name.Equals(PrevPlayerName))
                {
                    if (i + 1 >= this.Table.Players.Count)
                        return this.Table.Players[0];
                    else
                        return this.Table.Players[i + 1];
                }//if
            }//for

            throw new Exception("Player not found");
        }//Player NextPlayerTurn(string PName)

        private void DealCardsToPlayers()
        {
            int startingPosition = 0;  //this can be random or the player who won last game
            //deal cards to players
            int cardsDealt = 0;
            while (Deck.HasCard() && cardsDealt<Table.Players.Count*13)
            {
                Table.Players[startingPosition++].Hand.AddCardToHand(Deck.getNextCard());
                startingPosition = startingPosition % Table.Players.Count;
                cardsDealt++;
            }

            //send hand to players
            foreach (Player p in Table.Players)
                Server.DealHandToPlayer(p.Name, p.Hand);
        }
        #endregion



        #region Big2ServerService Event handlers
        public List<string> Big2Server_LoginCalled(string name)
        {
            //System.Diagnostics.EventLog.WriteEntry("Big2", "in add player to room- " + name);
            AddPlayerToRoom(name);

            List<string>temp=new List<string>();
            PlayersAndChatters.ForEach(delegate(Player p) { temp.Add(p.Name); });
            temp.Add(name);
            Server.PlayerJoin(name);
            return temp;            
        }

        public int Big2Server_TalkCalled(string name, string message)
        {
            Server.NewTalkMSG(message);
            return 0;
        }

        public int Big2Server_JoinGameCalled(string name)
        {
            
            if (Table.Players.Count >= GameRules.GetNumPlayers)
                return -1;
            Player player = PlayersAndChatters.Find(delegate(Player p) { return p.Name == name; });
            Table.Players.Add(player);
            if (Table.Players.Count == GameRules.GetNumPlayers)
            {
                //TODO: call back to client to let them know the game is full
                List<string> playersInGame=new List<string>();
                Table.Players.ForEach(delegate(Player p){playersInGame.Add(p.Name);});
                Server.GameStarted(playersInGame);
                thread = new Thread(new ThreadStart(StartGame));
                thread.Start();
            }
            return 0;
        }

        public int Big2Server_PlayCardCalled(string name, List<Card> cardsToPlay)
        {
            //check if cards played are valid, if not return -1
            //check if cards played is bigger than current play, if not return -2
            //create Play object from cards play, and set cards played the current play
            //notify each user about the new current play
            //check of user wins or not
            // if yes end game, if not, if not, then next player turn
            
            Player currentPlayer=ListHelper.FindPlayerByName(Table.Players,name);

            int cardsLeftAfterPlay = currentPlayer.Hand.CardsLeft() - cardsToPlay.Count;
            Play p = new Play(cardsToPlay, currentPlayer.Name, cardsLeftAfterPlay == 1);

            if (Table.CurrentPlay != null && name != Table.CurrentPlay.PlayerName && p.ComparePlay(Table.CurrentPlay) <= 0)
                return -2; // play is not big enough to beat current play

            //notify each player about the current play
            Thread t = null;
            foreach (Player player in Table.Players)
            {
                t = new Thread(new ThreadStart(delegate()
                {
                    Server.CurrentPlay(player.Name, p);
                }
                ));
                t.Start();
            }

            //remove the played card from the player's hand
            CardHelper.RemoveCards(currentPlayer.Hand.CardsList, cardsToPlay);

            
            if (cardsLeftAfterPlay <= 0)
            {
                PlayerWins(currentPlayer);
            }
            else
            {


                t = new Thread(new ThreadStart(delegate()
                {
                    NextPlayerTurn(FindNextPlayer(Table.CurrentTurnPlayerName), p);
                }
                ));
                t.Start();
            }
            return 0;
        }

        public int Big2Server_PassCalled(string name)
        {
            Player p=FindNextPlayer(name);
            NextPlayerTurn(p, Table.CurrentPlay);
            return 0;
        }

        public int Big2Server_LogoutCalled(string name)
        {
            return 1;
        }

        public int Big2ServerService_StartGameCalled(string name)
        {
            numPlayerReady++;
            if (numPlayerReady == Table.Players.Count)
            {
                StartGame();
                numPlayerReady = 0;
            }
            return 0;
        }

        #endregion
    }
}
